/*=============================================================================
PostProcessUtils.h : Post processing common utilities
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* 18/06/2005: Re-organized (to minimize code dependencies/less annoying compiling times)
* Created by Tiago Sousa

=============================================================================*/

#ifndef _POSTPROCESSUTILS_H_
#define _POSTPROCESSUTILS_H_

struct SD3DSurface;
class CShader;

struct SPostEffectsUtils
{
  // Create all resources
  static bool Create();

  // Release all used resources
  static void Release();

  // Create a render target
  static bool CreateRenderTarget(const char *szTexName, CTexture *&pTex, int iWidth, int iHeight, bool bUseAlpha, bool bMipMaps = 0, ETEX_Format pTexFormat = eTF_A8R8G8B8, int nCustomID = -1, int nFlags = 0, int8 nPriority=-1);

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Utilities to void some code duplication
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  // Begins render pass utility - for post process stuff only pass 0 assumed to be used
  static void ShBeginPass( CShader *pShader, const CCryNameTSCRC& TechName, uint32 nFlags = 0);

  // Ends render pass utility
  static void ShEndPass();

  // Set vertex shader constant utility
  static void ShSetParamVS( const CCryNameR& pParamName, const Vec4 &pParam );

  // Set pixel shader constant utility
  static void ShSetParamPS( const CCryNameR& pParamName, const Vec4 &pParam );

	// Draws fullscreen aligned triangle
  static void DrawFullScreenTriangle(int nTexWidth, int nTexHeight, float z=0);

  // Draws fullscreen aligned quad
  static void DrawFullScreenQuad(int nTexWidth, int nTexHeight, float z=0, bool bAllowRectList = true, const RECT * pSrcRegion = NULL);

  // Draws screen aligned quad
  static void DrawScreenQuad(int nTexWidth, int nTexHeight, float x0=0, float y0=0, float x1=1, float y1=1);
  
  // Draws a border to fullscreen
  static void DrawFullScreenBorder(int nTexWidth, int nTexHeight, const Vec4& vSpaces, bool bTrapeze = true);	//left up right down
  static void DrawFullScreenBorder(int nTexWidth, int nTexHeight, const Vec2& vLeftUp, const Vec2& vRightDown, bool bTrapeze = true){	DrawFullScreenBorder(nTexWidth, nTexHeight, Vec4(vLeftUp.x, vLeftUp.y, vRightDown.x, vRightDown.y), bTrapeze);	}
  static void DrawFullScreenBorder(int nTexWidth, int nTexHeight, float x0, float y0, float x1, float y1, bool bTrapeze = true)		{	DrawFullScreenBorder(nTexWidth, nTexHeight, Vec4(x0, y0, x1, y1), bTrapeze);	}

  // Draws a generic, non-screen-aligned quad
  static void DrawQuad(int nTexWidth, int nTexHeight,
	  const Vec2& vxA, const Vec2& vxB, const Vec2& vxC, const Vec2& vxD,
	  const Vec2& uvA=Vec2(0,0), const Vec2& uvB=Vec2(0,1), const Vec2& uvC=Vec2(1,1), const Vec2& uvD=Vec2(1,0) );


  // Draws full-screen aligned quad and pass also camera edges (useful for world space reconstruction)
  static void DrawFullScreenQuadWPOS(int nTexWidth, int nTexHeight, float z=0);
  static void DrawScreenQuadWPOS(float x, float y, float sx, float sy, float z, int nTexWidth, int nTexHeight);

  // Sets a texture
  static void SetTexture(CTexture *pTex, int nStage, int nFilter=FILTER_LINEAR, int nClamp=1, bool bSRGBLookup = false, DWORD dwBorderColor = 0);

  // Copy a texture into other texture
  virtual void StretchRect(CTexture *pSrc, CTexture *&pDst, bool bCoarseDepthInAlpha = false, bool bDecodeSrcRGBK = false, bool bEncodeDstRGBK = false, bool bBigDownsample = false, bool bMaxDownsample = false, bool bBindMultisampled = false) = 0;

  // Copy screen into texture
  virtual void CopyScreenToTexture( CTexture *&pDst, const RECT * pSrcRect ) = 0;

  // Apply Gaussian blur a texture
  virtual void TexBlurGaussian(CTexture *pTex, int nAmount=1, float fScale=1.0f, float fDistribution=5.0f, bool bAlphaOnly = false, CTexture *pMask = 0, bool bSRGB = false, CTexture *pBlurTmp = 0) = 0;
  
	// A 2x2 bilateral kernel is used to bi-linearly sample the source texture effectively sampling 16 pixels.
	// The same function can be used to perform low-quality upscaling.
	virtual void BilateralBlurring(CTexture *pSrc, CTexture *pDest) = 0;

  // Clear active render target region
  static void ClearScreen(float r, float g, float b, float a);

  // Reset texture states
  static void ResetTexStages()
  {
    for (int i=0; i<MAX_TMU; i++)
    {
      CTexture::s_TexStages[i].m_Texture = NULL;
    }
  }

	static void UpdateFrustrumCorners();

  // Log utility
  static void Log(char *pszMsg)
  {
    if(gRenDev->m_LogFile && pszMsg)
    {
      gRenDev->Logv(SRendItem::m_RecurseLevel[gRenDev->m_pRT->GetThreadList()], pszMsg);
    }
  }

  // Get current color matrix set up by global color parameters
  Matrix44 &GetColorMatrix();

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Math utils
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  // Linear interpolation
  static float InterpolateLinear(float p1, float p2, float t) 
  { 
    return p1 + (p2 - p1) * t; 
  };

  // Cubic interpolation
  static float InterpolateCubic(float p1, float p2, float p3, float p4, float t) 
  { 
    float t2 = t*t; 
    return (((-p1 * 2.0f) + (p2 * 5.0f) - (p3 * 4.0f) + p4) / 6.0f) * t2 * t + (p1 + p3 - (2.0f * p2)) * t2 + (((-4.0f * p1) + p2 + (p3 * 4.0f) - p4) / 6.0f) * t + p2; 
  };

  // Sine interpolation
  static float InterpolateSine(float p1, float p2, float p3, float p4, float t) 
  { 
    return p2 + (t * (p3 - p2)) + (sinf(t * PI) * ((p2 + p2) - p1 - p3 + (t * (p1-(p2+p2+p2) + (p3 + p3 + p3) - p4))) / 8.0f); 
  };

  // Return normalized random number
  static float randf()
  {
    return Random();
  }

  // Return signed normalized random number
  static float srandf()
  {
    return Random() *  2.0f - 1.0f ;
  }

  // Returns closest power of 2 size
  static int GetClosestPow2Size(int size)
  {
    float fPower= floorf(logf((float)size)/logf(2.0f));
    int nResize=int(powf(2.0f, fPower));

    // Clamp
    if( nResize >= 512)
    {
      nResize = 512;
    }

    return nResize;
  }

	static void GetTextureRect(CTexture *pTexture, RECT* pRect)
	{
		pRect->left = 0;
		pRect->top = 0;
		pRect->right = pTexture->GetWidth();
		pRect->bottom = pTexture->GetHeight();
	}

  static float GaussianDistribution1D(float x, float rho)
  {
    float g = 1.0f / ( rho * sqrtf(2.0f * PI)); 
    g *= expf( -(x * x)/(2.0f * rho * rho) );
    return g;
  }

  static float GaussianDistribution2D( float x, float y, float rho )
  {
	  float g = 1.0f / (2.0f * PI * rho * rho);
	  g *= expf( -(x*x + y*y)/(2*rho*rho) );
	  return g;
  }


	static CTexture *GetMsaaRT(bool bCurrentFrame = true)
	{
		if( CRenderer::CV_r_PostAA )
		{
			return CTexture::s_ptexPrevBackBuffer[ bCurrentFrame?	 SPostEffectsUtils::m_iFrameCounter %2 : (SPostEffectsUtils::m_iFrameCounter  + 1) % 2 ];   
		}

		return 0;
	}

public:

  static SD3DSurface *m_pCurDepthSurface;
  static RECT m_pScreenRect;
  static ITimer *m_pTimer;           
  static int m_iFrameCounter;
  static int m_nColorMatrixFrameID;
  
  static CShader *m_pCurrShader;

  Matrix44 m_pView;
  Matrix44 m_pProj;
  Matrix44 m_pViewProj;

  Matrix44 m_pColorMat;
  static float m_fWaterLevel;

	// frustrum corners
	static Vec3 m_vRT, m_vLT, m_vLB, m_vRB;
	static int m_nFrustrumFrameID;

protected:
  
  SPostEffectsUtils()
	{
		m_pView.SetIdentity();
		m_pProj.SetIdentity();
		m_pViewProj.SetIdentity();
		m_pColorMat.SetIdentity();

		m_pCurDepthSurface = NULL;
		m_pScreenRect.left = m_pScreenRect.top = 0;
		m_pScreenRect.bottom = m_pScreenRect.right = 0;
		m_pTimer = NULL;           
		m_iFrameCounter = 0;
		m_nColorMatrixFrameID = -1;

		m_pCurrShader = NULL;
		m_fWaterLevel = 0.0;		
		m_vRT = m_vLT = m_vLB = m_vRB = Vec3(ZERO);
		m_nFrustrumFrameID = -1;
	}

  virtual ~SPostEffectsUtils() 
  { 
  }


};

#endif
